From abaff6993d0492eb06a7532085f60db7fca43b8a Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Wed, 28 Mar 2007 20:19:11 +0100 Subject: [PATCH] pciback: Devices can lose config space info on D3->D0 transition, yet they may not (and even if they try, can not) restore the BARs. Since Linux does not do this for us, pciback must do it for itself manually. Possible todo: Restore all first 64 bytes of the config space? Signed-off-by: Alex Williamson Signed-off-by: Keir Fraser --- .../xen/pciback/conf_space_capability_pm.c | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c index d92262d1c3..1cdc6e9303 100644 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c @@ -32,19 +32,19 @@ static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, void *data) { int err; - u16 cur_value; - pci_power_t new_state; + u16 old_value; + pci_power_t new_state, old_state; - /* Handle setting power state separately */ - new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); - - err = pci_read_config_word(dev, offset, &cur_value); + err = pci_read_config_word(dev, offset, &old_value); if (err) goto out; + old_state = (pci_power_t)(old_value & PCI_PM_CTRL_STATE_MASK); + new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); + new_value &= PM_OK_BITS; - if ((cur_value & PM_OK_BITS) != new_value) { - new_value = (cur_value & ~PM_OK_BITS) | new_value; + if ((old_value & PM_OK_BITS) != new_value) { + new_value = (old_value & ~PM_OK_BITS) | new_value; err = pci_write_config_word(dev, offset, new_value); if (err) goto out; @@ -53,10 +53,25 @@ static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, /* Let pci core handle the power management change */ dev_dbg(&dev->dev, "set power state to %x\n", new_state); err = pci_set_power_state(dev, new_state); - if (err) + if (err) { err = PCIBIOS_SET_FAILED; + goto out; + } - out: + /* + * Device may lose PCI config info on D3->D0 transition. This + * is a problem for some guests which will not reset BARs. Even + * those that have a go will be foiled by our BAR-write handler + * which will discard the write! Since Linux won't re-init + * the config space automatically in all cases, we do it here. + * Future: Should we re-initialise all first 64 bytes of config space? + */ + if (new_state == PCI_D0 && + (old_state == PCI_D3hot || old_state == PCI_D3cold) && + !(old_value & PCI_PM_CTRL_NO_SOFT_RESET)) + pci_restore_bars(dev); + + out: return err; } -- 2.30.2